const request = require('superagent');
const mongo = require('../../app/lib/mongo');
const cheerio = require('cheerio');

class SESAME{
    constructor(db, ip){
        this.db = db;
        this.whiteIP = ip;
    }
    static __self__randomString (len = 16){
        let chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
        let res = "";
        for (let i = 0; i < len; i++) {
            let id = Math.ceil(Math.random() * 35);
            res += chars[id];
        }
        return res;
    }
    static __self__generateCookiesString(array){
        let arr = [];
        for(let co of array){
            let cookie = co.split(";");
            if(cookie[0].indexOf("delete")<0)arr.push(cookie[0])
        }
        return arr.join("; ")
    }

    /**
     * 获取代理IP
     * @param host
     * @param url
     * @param cacheUrl
     * @returns {Promise<*>}
     */
    async getProxyIP(host, url, cacheUrl){
        try{
            let proxyIpCache = new mongo(this.db, 'backend.proxy.ip.cache');
            let timestamp = new Date(new Date().getTime() + 10 * 1000);
            console.log('cacheUrl', cacheUrl);
            let tempProxy = await proxyIpCache.findOne({url:cacheUrl, expire_time:{$gt:timestamp}}), proxyInfo;
            if(!tempProxy){
                proxyInfo = await this.getIP(host);
                delete proxyInfo._id;
                await proxyIpCache.update({url:cacheUrl}, {$set:proxyInfo});
            } else {
                proxyInfo = tempProxy;
            }
            return proxyInfo;
        }catch (e){
            console.error(e)
        }
    }

    /**
     * 获取新的代理IP
     * @returns {Promise<void>}
     */
    async getIP(host, tryTimes = 0){
        try{
            let sesameAccount = new mongo(this.db, 'backend.zhima.account');
            let proxyIp = new mongo(this.db, 'backend.proxy.ip'), proxyInfo;
            let query={$or:[], expire_time:{$gt:new Date(new Date().getTime() + 10 * 1000)}}, update={};
            query.$or.push({[`web.${host}`]:{$lte:10}}, {web:null});
            update[`web.${host}`] = 1;
            update[`use`] = 1;
            console.log(query);
            proxyInfo = await proxyIp.findOneAndUpdate(query, {$inc:update}, {upsert: false});
            console.log(proxyInfo);
            if(!proxyInfo.value){
                let account = await this.getSesameIP();
                if(!account) account = await this.getSesameAccount();
                console.log("account", account);
                await this.getSesameIPList(account);
                tryTimes++;
                return await this.getIP(host, tryTimes);
            } else {
                if(proxyInfo.value.tel){
                    let acoountInfo = await sesameAccount.findOne({tel:proxyInfo.value.tel});
                    console.log(acoountInfo);
                    if(acoountInfo.ipCount>19) {
                        await this.removeTel(proxyInfo.value.tel);
                        return await this.getIP(host, tryTimes);
                    }
                }
            }
            return proxyInfo.value;
        }catch(e){
            throw Error(e);
        }
    }

    /**
     * 获取当天ipcount小于20的账户
     * @returns {Promise<null>}
     */
    async getSesameIP (){
        try{
            const sesameAccount = new mongo(this.db, 'backend.zhima.account');
            let today = parseInt(new Date().getTime()/(1000*60*60*24));
            let hasIPAccount = await sesameAccount.findOne({lastTime:today, ipCount:{$lt:20}});
            if(!hasIPAccount) return null;
            return hasIPAccount;
        }catch(e){
            console.error(e);
        }
    }

    /**
     * 登录芝麻账户并获取今日免费额度
     * @returns {Promise<*>}
     */
    async getSesameAccount(account = null){
        try{
            if(!account){
                const sesameAccount = new mongo(this.db, 'backend.zhima.account');
                let today = parseInt(new Date().getTime()/(1000*60*60*24));
                let accountNum = await sesameAccount.count({$or:[{lastTime:{$ne:today}}, {lastTime:null}]});
                account = await sesameAccount.findOneAndUpdate({$or:[{lastTime:{$ne:today}}, {lastTime:null}]},{$set:{lastTime: today, ipCount:0}}, {skip:Math.ceil(Math.random() * accountNum) - 1});


                // let account = await this.findOne({appkey:null});

                if(!account.value) throw new Error("已经没有可用的芝麻账户");
                account = account.value;
            }

            // if(!account) throw new Error("已经没有可用的芝麻账户");

            //登录账户
            console.log("登录账户");
            let header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'};
            let indexReq = await request.get('http://web.http.cnapi.cc/core/api/verify').set(header);
            let jsonCallback = `jQuery${SESAME.__self__randomString()}_${new Date().getTime()}`;
            header.cookie = SESAME.__self__generateCookiesString(indexReq.headers["set-cookie"]);
            header.Referer = 'http://h.zhimaruanjian.com/';
            let loginReq = await request.get(`http://web.http.cnapi.cc/index/users/login_do?jsonpcallback=${jsonCallback}&phone=${account.tel}&password=${account.password}&remember=false&_=${new Date().getTime()}`).set(header);
            let reg = new RegExp(`^${jsonCallback}\\("(.*)"\\)$`);
            let callbackBody = loginReq.text.match(reg) ? JSON.parse(loginReq.text.match(reg)[1].replace(/\\"/g,"\"")) : {};
            console.log(callbackBody.msg);
            if(callbackBody.code !== "1") throw new Error("账户登录错误:" + JSON.stringify(callbackBody));
            header.cookie = SESAME.__self__generateCookiesString(loginReq.headers["set-cookie"]);
            header.Referer = 'http://h.zhimaruanjian.com/ucenter/';

            //如果不存在account.user_id，则获取用户信息
            if(!account.user_id){
                let userInfoJsonCallback = `jQuery${SESAME.__self__randomString()}_${new Date().getTime()}`;
                let userInfoReq = await request.get(`http://web.http.cnapi.cc/index/users/user_info?jsonpcallback=${userInfoJsonCallback}&_=${new Date().getTime()}`).set(header);
                let userInfoReg = new RegExp(`^${userInfoJsonCallback}\\("(.*)"\\)$`);
                let userInfoCallbackBody = userInfoReq.text.match(userInfoReg) ? JSON.parse(userInfoReq.text.match(userInfoReg)[1].replace(/\\"/g,"\"")) : {};
                console.log("获取用户信息", userInfoCallbackBody.msg);
                if(userInfoCallbackBody.code !== "1") throw new Error("获取账户错误:" + JSON.stringify(userInfoCallbackBody));
                await sesameAccount.update({tel:account.tel}, {$set:{user_id:userInfoCallbackBody.ret_data.id}});
                account.user_id = userInfoCallbackBody.ret_data.id;
            }

            //如果不存在account.appkey，则获取
            if(!account.appkey){
                let appInfoReq = await request.get(`http://web.http.cnapi.cc/index/index/get_white_link`).set(header);
                let appkey = appInfoReq.text.match(/appkey=([a-z0-9A-Z]+)/)[1];
                console.log(appkey);
                await sesameAccount.update({tel:account.tel}, {$set:{appkey}});
                account.appkey = appkey;
            }

            //获取今日免费额度
            // if(account.lastTime!==today){
                console.log("获取今日免费额度");
                let freePackJsonCallback = `jQuery${SESAME.__self__randomString()}_${new Date().getTime()}`;
                let freeReg = new RegExp(`^${freePackJsonCallback}\\("(.*)"\\)$`);
                console.log(SESAME.__self__generateCookiesString(loginReq.headers["set-cookie"]));
                let freePack = await request.get(`http://web.http.cnapi.cc/index/users/get_day_free_pack?jsonpcallback=${freePackJsonCallback}&_=${new Date().getTime()}`).set(header);
                let freePackBody = freePack.text.match(freeReg) ? JSON.parse(freePack.text.match(freeReg)[1].replace(/\\"/g,"\"")) : {};
                console.log("获取今日免费额度", freePackBody.msg);
                if(freePackBody.code !=="1") throw new Error("获取免费额度错误:" + JSON.stringify(freePackBody));
            // }

            //获取今日免费额度的pack id
            if(!account.pack_id){
                header.Referer = 'http://h.zhimaruanjian.com/getapi/';
                let packInfoJsonCallback = `jQuery${SESAME.__self__randomString()}_${new Date().getTime()}`;
                let packInfoReg = new RegExp(`^${packInfoJsonCallback}\\("(.*)"\\)$`);
                let packInfoReq = await request.get(`http://web.http.cnapi.cc/index/api/get_package_info?jsonpcallback=${packInfoJsonCallback}&mid=${account.user_id}&_=${new Date().getTime()}`).set(header);
                console.log(packInfoReq.text);
                let packInfoReqBody;
                if(packInfoReq.text.match(packInfoReg)){
                    let stringForParse = unescape(packInfoReq.text).match(packInfoReg)[1].replace(/\\"/g,"\"").replace(/\\\\/g,"\\");
                    console.log(stringForParse);
                    packInfoReqBody = JSON.parse(stringForParse);
                } else {
                    packInfoReqBody = {}
                }
                if(packInfoReqBody.code !=="1") throw new Error("获取免费包id错误:" + JSON.stringify(freePackBody));
                let $ = cheerio.load(packInfoReqBody.ret_data.list);
                let packageList = $("li.savecookie"), pack_id;
                if(packageList.length > 0){
                    for(let i = 0;i<packageList.length;i++){
                        let id = packageList.eq(i).data("id");
                        console.log("pack id", id);
                        if(!id) continue;
                        pack_id = id;
                        account.pack_id = id;
                        await sesameAccount.update({tel:account.tel}, {$set:{pack_id}});
                        break;
                    }
                } else {
                    throw new Error("未获得免费包信息");
                }
            }
            return account;
        }catch(e){
            console.error(e)
        }
    }

    /**
     * 获取package内ip
     * @param account
     * @returns {Promise<void>}
     */
    async getSesameIPList (account) {
        try{
            let whiteLink = `http://web.http.cnapi.cc/index/index/save_white?neek=${account.user_id}&appkey=${account.appkey}&white=${this.whiteIP}`;
            await request.get(whiteLink);
            let ipApi = `http://webapi.http.zhimacangku.com/getip?num=20&type=2&pro=0&city=0&yys=0&port=1&pack=${account.pack_id}&ts=1&ys=1&cs=1&lb=1&sb=0&pb=45&mr=2&regions=`;
            let proxyIp = new mongo(this.db, 'backend.proxy.ip');
            let list = await request.get(ipApi);
            let body = JSON.parse(list.text);
            console.log(body);
            if(body.code === 0 ){
                let listAll = body.data;
                for(let ip of listAll){
                    await proxyIp.update({ip:ip.ip, port:ip.port}, {$set:{expire_time:new Date(ip.expire_time), isSesame:true, tel:account.tel}})
                }
            } else {
                switch(body.code){
                    case 121:
                        await this.getSesameAccount(account);
                        await this.getSesameIPList(account);
                        break;
                    case 116:
                        await this.setFinishCount(account.tel);
                        account = await this.getSesameAccount();
                        await this.getSesameIPList(account);
                        break;
                    default:
                        throw Error(body.msg);
                }
            }
        }catch(e){
            console.error(e);
        }
    }

    /**
     * 删除无法连接的IP
     * @param ip
     * @param port
     * @returns {Promise<void>}
     */
    async removeIP(ip, port){
        try {
            const proxyIpCache = new mongo(this.db, 'backend.proxy.ip.cache');
            const proxyIp = new mongo(this.db, 'backend.proxy.ip');
            await proxyIpCache.remove({ip, port});
            await proxyIp.remove({ip, port});
        } catch (e) {
            console.error(e);
        }
    }

    /**
     * 删除账户的ip
     * @param tel
     * @returns {Promise<void>}
     */
    async removeTel(tel){
        try {
            const proxyIpCache = new mongo(this.db, 'backend.proxy.ip.cache');
            const proxyIp = new mongo(this.db, 'backend.proxy.ip');
            await proxyIpCache.remove({tel});
            await proxyIp.remove({tel});
        } catch (e) {
            console.error(e);
        }
    }

    async raiseIpCount(tel, num = 1){
        try{
            const sesameAccount = new mongo(this.db, 'backend.zhima.account');
            await sesameAccount.update({tel}, {$inc:{ipCount:num}})
        }catch (e) {
            throw Error(e);
        }
    }

    async setFinishCount(tel){
        try{
            const sesameAccount = new mongo(this.db, 'backend.zhima.account');
            await sesameAccount.update({tel}, {$set:{ipCount:20}})
        }catch (e) {
            throw Error(e);
        }
    }
}

module.exports = SESAME;